package se.webbzon.oschi01.sound;

import java.io.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.Mixer.Info;

import se.webbzon.boltzmann.debug.Debug;

public class DataLineSound implements FiniteSound {
	
	// The default mixer to use for the platform
	private static Info defaultMixer = null;
	
	// The size of the buffer 
	private static final int bufferSize = 8192;
	
	// The sound thread
	private final SoundThread thread;
	
	// The data line used for playing the sound
	private final SourceDataLine line;
			
	// The input stream from where the sound is read
	private final InputStream is;
	
	// The source of the sound
	private final DataLineSource source;
	
	// Whatever the sound is playing
	private boolean paused, stopped;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public DataLineSound(DataLineSource source) throws IOException, LineUnavailableException {
		if (defaultMixer == null)
			this.line = AudioSystem.getSourceDataLine(source.getFormat());
		else try {
			this.line = AudioSystem.getSourceDataLine(source.getFormat(),defaultMixer);
		} catch (Exception e) {
			throw new IOException(e);
		}
		this.source = source;
		this.is = source.createInputStream();
		line.open(source.getFormat());
		paused = true;
		stopped = false;
		thread = new SoundThread();
		thread.start();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Sets the default mixer for all new ClipSounds. **/
	public static final void setDefaultMixer(Info defaultMixer) {
		DataLineSound.defaultMixer = defaultMixer;
	}
	
	/** Returns the default mixer for all new ClipSounds. **/
	public static final Info getDefaultMixer() {
		return defaultMixer;
	}
	
	/** Returns true if the sound has been stopped. **/
	public synchronized boolean isStopped() {
		return stopped;
	}

	/** Returns true if the sound has been paused. **/
	public synchronized boolean isPaused() {
		return paused;
	}
	
	@Override public synchronized void play() {
		this.paused = false;
	}

	@Override public synchronized boolean isPlaying() {
		return !paused && !stopped;
	}

	@Override public synchronized void pause() {
		this.paused = true;
	}

	@Override public synchronized void stop() {
		this.stopped = true;
	}
	
	@Override public void setLooping(boolean looping) {
		
	}

	@Override public boolean isLooping() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override public long length() {
		int samples = source.getSize() / source.getFormat().getFrameSize();
		double seconds = samples / source.getFormat().getSampleRate();
		return (long) (seconds * 1000000);
	}

	@Override public long pos() {
		return -1;
	}

	@Override public boolean seek(long time) {
		return false;
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	private final class SoundThread extends Thread {

		@Override public void run() {
			line.start();
			final int total = source.getSize();
			final byte[] buffer = new byte[bufferSize];
			int read = 0;
			int available;
			try {
				while (read < total && !isStopped()) {
					available = is.read(buffer, 0, bufferSize);
		    		if (available == -1) break;
		    		read += available; 
		    		line.write(buffer, 0, available);
		    		while (isPaused()) {};
				}
			} catch (IOException e) {
				Debug.report(e);
			}
			
			line.drain();
			line.stop();
			line.close();
		}
	
		
	}

	

}
